﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Emgu;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;
using Emgu.CV.VideoSurveillance;

namespace GridCut.Scaffold
{
	public class BGSubtractor
	{
		BackgroundSubtractorMOG2 bgModel;
		IImageTransformer trans;
		Mat fgMask;
		public BGSubtractor(IImageTransformer trans = null)
		{
			bgModel = new BackgroundSubtractorMOG2();
			this.trans = trans;
		}
		public void Fit(string folder)
		{
			Console.Write("重新训练 MOG2 ...");
			fgMask = new Mat();
			var imgs = Directory.EnumerateFiles(folder).ToList();
			for (int i = 0; i < imgs.Count; i++)
			{
				Mat rd = CvInvoke.Imread(imgs[i]);
				if (trans != null)
					rd = trans.Transform(rd);
				bgModel.Apply(rd, fgMask, 1/(1+i));
			}
			Console.WriteLine("Done!");
		}
		public void Extract(IInputArray src, IOutputArray mask, IOutputArray fgimg=null)
		{
			if (trans == null)
				bgModel.Apply(src, fgMask, 0);
			else
				bgModel.Apply(trans.Transform(src), fgMask, 0);
			CvInvoke.Threshold(fgMask, fgMask, 200, 255, ThresholdType.Binary);
			var ker2 = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(10, 1),new Point(-1,-1));
			var ker3 = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(10, 4),new Point(-1,-1));

			CvInvoke.Dilate(fgMask, fgMask, ker2, new Point(-1,-1), 1, BorderType.Constant, new Emgu.CV.Structure.MCvScalar(0));
			CvInvoke.Erode(fgMask, fgMask, ker3, new Point(-1,-1), 1, BorderType.Constant, new Emgu.CV.Structure.MCvScalar(0));

			Mat dst = new Mat(fgMask.Rows, fgMask.Cols, DepthType.Cv8U, 1);
			dst.SetTo(new Emgu.CV.Structure.MCvScalar(0));

			VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
			Mat hie = new Mat();
			CvInvoke.FindContours(fgMask, contours, hie, RetrType.External, ChainApproxMethod.ChainApproxSimple);
			for (int i = 0; i < contours.Size; i++)
			{
				VectorOfVectorOfPoint approx = new VectorOfVectorOfPoint(1);
				CvInvoke.ConvexHull(contours[i], approx[0], true);
				double a = CvInvoke.ContourArea(approx[0]);
				if (a > 8000)
				{
					CvInvoke.DrawContours(dst, approx, 0, new Emgu.CV.Structure.MCvScalar(255,255,255), -1);
				}
			}
			dst.CopyTo(mask);
			if(fgimg!=null)
				src.GetInputArray().CopyTo(fgimg, mask);
		}
	}
}
